﻿using System;
using System.IO;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using mmCore.Common;

namespace mmGoogleDataProvider
{
    public class Gutils
    {
        public const string DataFileName = "mmanagerdatafile";
        public const string ServiceName = "MovieManager";
        public const string TempDataFile = "tempmmanager.txt";

        static public string EncodeTo64(string toEncode)
        {
            var toEncodeAsBytes = System.Text.Encoding.UTF8.GetBytes(toEncode);
            return EncodeTo64(toEncodeAsBytes);
        }

        static public byte[] EncodeTo64ByteArray(byte[] toEncode)
        {
            var returnValue = Convert.ToBase64String(toEncode);
            return System.Text.Encoding.UTF8.GetBytes(returnValue);
        }

        static public string EncodeTo64(byte[] toEncode)
        {
            var returnValue = Convert.ToBase64String(toEncode);
            return returnValue;
        }

        static public string DecodeFrom64(string encodedData)
        {
          var encodedDataAsBytes = Convert.FromBase64String(encodedData);
          var returnValue = System.Text.Encoding.UTF8.GetString(encodedDataAsBytes);
          return returnValue;
        }

        static public byte[] DecodeFrom64ToByteArray(string encodedData)
        {
            return Convert.FromBase64String(encodedData);
        }

        static public byte[] DecodeFrom64ToByteArray(byte [] encodedData)
        {
            return Convert.FromBase64String(System.Text.Encoding.UTF8.GetString(encodedData));
        }

        public static bool CompressFile(string inputFile, string outputFile)
        {
            if(!File.Exists(inputFile))
            {
                return false;
            }
            var fsOut = File.Create(outputFile);
            var zipStream = new ZipOutputStream(fsOut);

            zipStream.SetLevel(9); //0-9, 9 being the highest level of compression

            var fi = new FileInfo(inputFile);

            var entryName = ZipEntry.CleanName(inputFile); // Removes drive from name and fixes slash direction
            var newEntry = new ZipEntry(entryName) {DateTime = fi.LastWriteTime, Size = fi.Length};

            zipStream.PutNextEntry(newEntry);

            var buffer = new byte[4096];
            using (var streamReader = File.OpenRead(inputFile))
            {
                StreamUtils.Copy(streamReader, zipStream, buffer);
            }
            zipStream.CloseEntry();
        
            zipStream.IsStreamOwner = true;	// Makes the Close also Close the underlying stream
            zipStream.Close();

            return true;
        }

        public static Stream CompressFile(Stream inputFile)
        {
            if (inputFile == null || !inputFile.CanRead)
            {
                ExceptionManager.ReportError(new Exception("Cannot read input stream."));
                return Stream.Null;
            }
            var fsOut = new MemoryStream();
            var zipStream = new ZipOutputStream(fsOut);

            zipStream.SetLevel(9);
            var newEntry = new ZipEntry(TempDataFile) {DateTime = DateTime.Now, Size = inputFile.Length};

            zipStream.PutNextEntry(newEntry);

            var buffer = new byte[4096];
            StreamUtils.Copy(inputFile, zipStream, buffer);
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = true;
            zipStream.Finish();
            fsOut.Seek(0, SeekOrigin.Begin);

            return fsOut;
        }

        public static Stream ExtractZipFile(Stream inStream)
        {
            var zf = new ZipFile(inStream);
            if(zf.Count != 1)
            {
                ExceptionManager.ReportError(new Exception("Zip archive contains more than 1 file."));
                return Stream.Null;
            }
            if(!zf[0].IsFile)
            {
                ExceptionManager.ReportError(new Exception("Zip archive doesn't contain any file."));
                return Stream.Null;
            }
            var zipStream = zf.GetInputStream(zf[0]);
            zf.IsStreamOwner = true;

            var buffer = new byte[4096];
            var streamWriter = new MemoryStream();
            StreamUtils.Copy(zipStream, streamWriter, buffer);
            zipStream.Close();

            return streamWriter;
        }

        public static Stream EncodeStream(Stream strin)
        {
            try
            {
                strin.Seek(0, SeekOrigin.Begin);
                var ms = new MemoryStream();
                using (var zs = CompressFile(strin))
                {
                    var buffer = new byte[zs.Length];
                    zs.Read(buffer, 0, (int)zs.Length);
                    var bb = EncodeTo64ByteArray(buffer);
                    ms.Write(bb, 0, bb.Length);
                    return ms;
                }
            }
            catch (Exception exc)
            {
                ExceptionManager.ReportError(exc);
                return Stream.Null;
            }
        }

        public static Stream DecodeStream(Stream strin)
        {
            try
            {
                strin.Seek(0, SeekOrigin.Begin);
                var bb = new byte[strin.Length];
                strin.Read(bb, 0, (int)strin.Length);
                var zb = DecodeFrom64ToByteArray(bb);
                var ms = new MemoryStream(zb, false);
                return ExtractZipFile(ms);
            }
            catch (Exception exc)
            {
                ExceptionManager.ReportError(exc);
                return Stream.Null;
            }
        }
    }
}
